package middlewares

import (
	"bytes"
	"ginfwk/infrastructure/component/logger"
	"ginfwk/infrastructure/facede"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"io"
	"time"
)

// 自定义writer
type CustomResponseWriter struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

func (w *CustomResponseWriter) Write(b []byte) (int, error) {
	w.body.Write(b) // 额外保存一份
	return w.ResponseWriter.Write(b)
}

func Logger() gin.HandlerFunc {

	return func(ctx *gin.Context) {
		// 获取 context-logger
		ctxLogger := facede.CtxLogger(ctx.Request.Context())

		start := time.Now()
		path := ctx.Request.URL.Path
		query := ctx.Request.URL.RawQuery
		reqBytes, reqBodyErr := ctx.GetRawData()
		if reqBodyErr != nil {
			ctxLogger.Warnf("read request body failed, err: %s", reqBodyErr.Error())
		}
		// 若读取到了数据,需要将读取指针还原
		if len(reqBytes) > 0 {
			ctx.Request.Body = io.NopCloser(bytes.NewBuffer(reqBytes))
		}

		// 记录請求日志
		ctxLogger.Desugar().Log(
			logger.RequestLevel,
			"http request start",
			zap.Int("status", ctx.Writer.Status()),
			zap.String("method", ctx.Request.Method),
			zap.String("content-type", ctx.ContentType()),
			zap.String("accept", ctx.GetHeader("accept")),
			zap.String("path", path),
			zap.String("query", query),
			zap.ByteString("request", reqBytes),
			zap.String("ip", ctx.ClientIP()),
			zap.String("user-agent", ctx.Request.UserAgent()),
		)

		// 使用自定义response-writer
		customWriter := &CustomResponseWriter{
			ResponseWriter: ctx.Writer,
			body:           bytes.NewBuffer([]byte{}),
		}
		ctx.Writer = customWriter

		// 处理请求
		ctx.Next()

		// Since returns the time elapsed since t.
		// It is shorthand for time.Now().Sub(t).
		cost := time.Now().Sub(start)

		// 记录响应日志
		rspBytes := customWriter.body.Bytes()
		ctxLogger.Desugar().Log(
			logger.RequestLevel,
			"http request end",
			zap.Int("status", ctx.Writer.Status()),
			zap.String("method", ctx.Request.Method),
			zap.String("content-type", ctx.Writer.Header().Get("content-type")),
			zap.String("path", path),
			zap.String("query", query),
			zap.ByteString("response", rspBytes),
			zap.String("ip", ctx.ClientIP()),
			zap.String("user-agent", ctx.Request.UserAgent()),
			zap.String("errors", ctx.Errors.ByType(gin.ErrorTypePrivate).String()),
			zap.Duration("cost", cost*1000), // 运行时间,单位:ms
		)

		// 后置中间件

		// 此处zap-logger库暂未解决此issues,可暂时忽略此错误
		// see: https://github.com/uber-go/zap/issues/880
		if err := ctxLogger.Sync(); err != nil {
			//log.Printf("flush context log falied, err: [%#v]", err)
		}
		if err := logger.Sync(); err != nil {
			//log.Printf("flush global log falied, err: [%#v]", err)
		}
	}
}
